﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.ComponentModel.Composition;

using NuMetaheuristics;
using NuMetaheuristics.Utility;

using MathNet.Numerics.Distributions;

namespace NuMetaheuristics.Operators.Value
{
    /// <summary>
    /// Modify a value using a gaussian curve with a given standard deviation. 
    /// The midified value is bounded by the given min and max values.
    /// </summary>
    /// <typeparam name="T">The value type to be modified</typeparam>
    [DataContract(Namespace = "http://numetaheuristics.codeplex.com/schemas")]
    abstract public class AbstractGaussianConvolver<T> : IUnaryOperator<T>, IDeserializationCallback
        where T : IComparable<T>
    {
        [DataMember]
        protected T _min;
        [DataMember]
        protected T _max;
        [DataMember]
        private double _stdDev;

        [DataMember]
        public double StdDev
        {
            get
            {
                return _stdDev;
            }
            set
            {
                _stdDev = value;
                _normal = new Normal(0.0, _stdDev);
            }
        }

        protected Normal _normal;

        public AbstractGaussianConvolver()
        {
            _normal = new Normal();
        }

        public AbstractGaussianConvolver(T min, T max, double stdDev)
        {
            _normal = new Normal(0.0, stdDev);
            _stdDev = stdDev;
            _min = min;
            _max = max;
        }

        public void OnDeserialization(object sender)
        {
            _normal = new Normal(0.0, _stdDev);
        }

        public void Operate(ref T value)
        {
            if (value.CompareTo(_min) < 0)
                throw new ArgumentOutOfRangeException("value", "Below min");

            if (value.CompareTo(_max) > 0)
                throw new ArgumentOutOfRangeException("value", "Above max");

            value = modifyValue(value);
        }

        abstract protected T modifyValue(T value);
    }

    [Export(typeof(IUnaryOperator<double>))]
    [DataContract(Namespace = "http://numetaheuristics.codeplex.com/schemas")]
    public class DoubleGaussianConvolver : AbstractGaussianConvolver<double>
    {
        public DoubleGaussianConvolver()
            : base()
        {
        }

        public DoubleGaussianConvolver(double min, double max, double stdDev)
            : base(min, max, stdDev)
        {
        }

        override protected double modifyValue(double value)
        {
            double sum = 0.0;
            do
            {
                double noise = _normal.Sample();
                sum = value + noise;
            } while (sum < _min || sum > _max);

            return sum;
        }
    }

    [Export(typeof(IUnaryOperator<int>))]
    [DataContract(Namespace = "http://numetaheuristics.codeplex.com/schemas")]
    public class IntegerGaussianConvolver : AbstractGaussianConvolver<int>
    {
        public IntegerGaussianConvolver()
            : base()
        {
        }

        public IntegerGaussianConvolver(int min, int max, double stdDev)
            : base(min, max, stdDev)
        {
        }

        override protected int modifyValue(int value)
        {
            int sum = 0;
            do
            {
                double noise = _normal.Sample();
                sum = value + (int)noise;
            } while (sum < _min || sum > _max);

            return sum;
        }
    }

}
